module	CoProcessor0(
	
	input			i_CP0_clk,
	input			i_CP0_enable,
	input			i_CP0_reset,
	//read
	input	[4:0]	i_CP0_raddr,
	input	[2:0]	i_CP0_rsel,
	output	[31:0]	o_CP0_rdata,
	//write
	input			i_CP0_wen,
	input	[4:0]	i_CP0_waddr,
	input	[2:0]	i_CP0_wsel,
	input	[31:0]	i_CP0_wdata,
	//CP0data
	output	[31:0]	o_CP0_Status,
	output	[31:0]	o_CP0_EPC,
	//COMMIT
	input			i_CP0_CMTvaild,
	input	[7:0]	i_CP0_CMTEXCCODE,
	input	[31:0]	i_CP0_CMTPC,
	input	[31:0]	i_CP0_CMTBadVAddr,
	
	input	[5:0]	i_CP0_interrupt,
	output			o_CP0_interrupt
);
	
	reg	[31:0]	Index;
	reg	[31:0]	Random;
	reg	[31:0]	EntryLo0;
	reg	[31:0]	EntryLo1;
	reg	[31:0]	Context;
	reg	[31:0]	PageMask;
	reg	[31:0]	Wired;
	reg	[31:0]	BadVAddr;
	reg	[31:0]	Count;
	reg	[31:0]	EntryHi;
	reg	[31:0]	Compare;
	reg	[31:0]	Status;
	reg	[31:0]	Cause;
	reg	[31:0]	EPC;
	reg	[31:0]	PRID;
	reg	[31:0]	EBase;
	reg	[31:0]	Config;
	reg	[31:0]	Config1;
	reg	[31:0]	TagLo;
	reg	[31:0]	TagHi;
	
	wire	[4:0]	rReg = i_CP0_raddr;
	wire	[2:0]	rSel = i_CP0_rsel;
	
	wire	[31:0]	rdata_Index 	= (rReg==5'd0 && rSel==3'd0) ? Index : 32'd0;	//Reg 0, Sel 0
	wire	[31:0]	rdata_Random 	= (rReg==5'd1 && rSel==3'd0) ? Random : 32'd0;	//Reg 1, Sel 0
	wire	[31:0]	rdata_EntryLo0 	= (rReg==5'd2 && rSel==3'd0) ? EntryLo0 : 32'd0;//Reg 2, Sel 0
	wire	[31:0]	rdata_EntryLo1 	= (rReg==5'd3 && rSel==3'd0) ? EntryLo1 : 32'd0;//Reg 3, Sel 0
	wire	[31:0]	rdata_Context 	= (rReg==5'd4 && rSel==3'd0) ? Context : 32'd0;	//Reg 4, Sel 0
	wire	[31:0]	rdata_PageMask 	= (rReg==5'd5 && rSel==3'd0) ? PageMask : 32'd0;//Reg 5, Sel 0
	wire	[31:0]	rdata_Wired 	= (rReg==5'd6 && rSel==3'd0) ? Wired : 32'd0;	//Reg 6, Sel 0
	wire	[31:0]	rdata_BadVAddr 	= (rReg==5'd8 && rSel==3'd0) ? BadVAddr : 32'd0;//Reg 8, Sel 0
	wire	[31:0]	rdata_Count 	= (rReg==5'd9 && rSel==3'd0) ? Count : 32'd0;	//Reg 9, Sel 0
	wire	[31:0]	rdata_EntryHi 	= (rReg==5'd10 && rSel==3'd0) ? EntryHi : 32'd0;//Reg 10, Sel 0
	wire	[31:0]	rdata_Compare 	= (rReg==5'd11 && rSel==3'd0) ? Compare : 32'd0;//Reg 11, Sel 0
	wire	[31:0]	rdata_Status 	= (rReg==5'd12 && rSel==3'd0) ? Status : 32'd0;	//Reg 12, Sel 0
	wire	[31:0]	rdata_Cause 	= (rReg==5'd13 && rSel==3'd0) ? Cause : 32'd0;	//Reg 13, Sel 0
	wire	[31:0]	rdata_EPC 		= (rReg==5'd14 && rSel==3'd0) ? EPC : 32'd0;	//Reg 14, Sel 0
	wire	[31:0]	rdata_PRID 		= (rReg==5'd15 && rSel==3'd0) ? PRID : 32'd0;	//Reg 15, Sel 0
	wire	[31:0]	rdata_EBase 	= (rReg==5'd15 && rSel==3'd1) ? EBase : 32'd0;	//Reg 15, Sel 1
	wire	[31:0]	rdata_Config 	= (rReg==5'd16 && rSel==3'd0) ? Config : 32'd0;	//Reg 16, Sel 0
	wire	[31:0]	rdata_Config1 	= (rReg==5'd16 && rSel==3'd1) ? Config1 : 32'd0;//Reg 16, Sel 1
	wire	[31:0]	rdata_TagLo 	= (rReg==5'd28 && ~rSel[0]) ? TagLo : 32'd0;	//Reg 28, Sel 0,2,4,6(even selects)
	wire	[31:0]	rdata_TagHi 	= (rReg==5'd29 && ~rSel[0]) ? TagHi : 32'd0;	//Reg 29, Sel 0,2,4,6(even selects)
	
	assign	o_CP0_rdata = rdata_Index 		| rdata_Random 	| rdata_EntryLo0 	| rdata_EntryLo1 	| rdata_Context 	|
						  rdata_PageMask 	| rdata_Wired 	| rdata_BadVAddr 	| rdata_Count 		| rdata_EntryHi 	|
						  rdata_Compare 	| rdata_Status 	| rdata_Cause 		| rdata_EPC 		| rdata_PRID 		|
						  rdata_EBase 		| rdata_Config 	| rdata_Config1 	| rdata_TagLo 		| rdata_TagHi;
	
	assign	o_CP0_Status = Status;
	assign	o_CP0_EPC	 = EPC;
	
	wire	ERET		= (i_CP0_CMTvaild && i_CP0_CMTEXCCODE[6]);
	wire	DelaySlot	= (i_CP0_CMTvaild && i_CP0_CMTEXCCODE[5]);
	wire	ExcCode		= (i_CP0_CMTvaild && i_CP0_CMTEXCCODE[4:0]);
	
	wire	[4:0]	wReg = i_CP0_waddr;
	wire	[2:0]	wSel = i_CP0_wsel;
	
	wire	TI = (compare==count);
	wire	HW5 = status[15] & (cause[15] | TI);
	wire	HW4 = status[14] & cause[14];
	wire	HW3 = status[13] & cause[13];
	wire	HW2 = status[12] & cause[12];
	wire	HW1 = status[11] & cause[11];
	wire	HW0 = status[10] & cause[10];
	wire	SW1 = status[9] & cause[9];
	wire	SW0 = status[8] & cause[8];
	
	wire	Exception	= ~status[1] && (i_CP0_CMTvaild && i_CP0_CMTEXCCODE[7]);
	wire	interrupt	= ~Status[1] & status[0] & (SW0|SW1|HW0|HW1|HW2|HW3|HW4|HW5);
	
always @(posedge i_CP0_clk)
	begin
		if(i_CP0_reset)
			begin
				//reset
				count 	<= 32'h0000_0000;
		        compare <= 32'hffff_ffff;
		        status	<= 32'h0040_0000;
				cause	<= 32'h0000_0000;
				epc		<= 32'hbfc0_0000;
			end
		else if(i_CP0_enable)
			begin
				if(interrupt)
					begin
						//interrupt
						Status[1] <= 1'd1;
						cause[31] <= DelaySlot;
						EPC	<= (DelaySlot) ? i_CP0_CMTPC - 32'd4 : i_CP0_CMTPC;	//!!!
						Cause[5:2] <= 5'd0;
					end
				else if(Exception)
					begin
						//Exception
						Status[1] <= 1'd1;
						cause[31] <= DelaySlot;
						EPC	<= (DelaySlot) ? i_CP0_CMTPC - 32'd4 : i_CP0_CMTPC;
						cause[6:2]	<= ExcCode;
						if(ExcCode==5'd4 || ExcCode==5'd5) BadVAddr <= i_CP0_CMTPC;
					end
				else 
					begin
						
						cause[30]	<= TI;
						status[1] 	<= (ERET) ? 1'b0 : status[1];
						if(~(i_CP0_wen && wReg==5'd9 && wSel==3'd0)) count <= count+32'd1;
						
						if(i_CP0_wen)
							begin
								if(wReg==5'd0 && wSel==3'd0) Index 		<= i_CP0_wdata;
								if(wReg==5'd1 && wSel==3'd0) Random 	<= i_CP0_wdata;  
								if(wReg==5'd2 && wSel==3'd0) EntryLo0 	<= i_CP0_wdata;  
								if(wReg==5'd3 && wSel==3'd0) EntryLo1 	<= i_CP0_wdata;  
								if(wReg==5'd4 && wSel==3'd0) Context 	<= i_CP0_wdata;  
								if(wReg==5'd5 && wSel==3'd0) PageMask 	<= i_CP0_wdata;  
								if(wReg==5'd6 && wSel==3'd0) Wired 		<= i_CP0_wdata;  
								//if(wReg==5'd8 && wSel==3'd0) BadVAddr 	<= i_CP0_wdata;  
								if(wReg==5'd9 && wSel==3'd0) Count 		<= i_CP0_wdata;  
								if(wReg==5'd10 && wSel==3'd0) EntryHi 	<= i_CP0_wdata;  
								if(wReg==5'd11 && wSel==3'd0) Compare 	<= i_CP0_wdata;  
								if(wReg==5'd12 && wSel==3'd0) Status 	<= {status[31:16],i_CP0_wdata[15:8],status[7:2],i_CP0_wdata[1:0]};  
								if(wReg==5'd13 && wSel==3'd0) Cause 	<= {cause[31:10],i_CP0_wdata[9:8],cause[7:0]};  
								if(wReg==5'd14 && wSel==3'd0) EPC 		<= i_CP0_wdata;  
								if(wReg==5'd15 && wSel==3'd0) PRID 		<= i_CP0_wdata;   
								if(wReg==5'd15 && wSel==3'd1) EBase 	<= i_CP0_wdata;   
								if(wReg==5'd16 && wSel==3'd0) Config 	<= i_CP0_wdata;   
								if(wReg==5'd16 && wSel==3'd1) Config1 	<= i_CP0_wdata;    
								if(wReg==5'd28 && ~wSel[0]) TagLo 		<= i_CP0_wdata;   
								if(rReg==5'd29 && ~wSel[0]) TagHi 		<= i_CP0_wdata; 
							end
					end
			end
	end

endmodule